/*
 * Copyright Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the authors tag. All rights reserved.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License version 2.
 * 
 * This particular file is subject to the "Classpath" exception as provided in the 
 * LICENSE file that accompanied this code.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */
package org.eclipse.ceylon.compiler.java.tools;

import org.eclipse.ceylon.common.StatusPrinter;
import org.eclipse.ceylon.compiler.java.codegen.CeylonFileObject;
import org.eclipse.ceylon.javax.tools.JavaFileObject;
import org.eclipse.ceylon.langtools.tools.javac.main.Option;
import org.eclipse.ceylon.langtools.tools.javac.util.Context;
import org.eclipse.ceylon.langtools.tools.javac.util.DiagnosticSource;
import org.eclipse.ceylon.langtools.tools.javac.util.JCDiagnostic;
import org.eclipse.ceylon.langtools.tools.javac.util.Log;
import org.eclipse.ceylon.langtools.tools.javac.util.Options;
import org.eclipse.ceylon.langtools.tools.javac.util.SourceLanguage;
import org.eclipse.ceylon.langtools.tools.javac.util.JCDiagnostic.DiagnosticType;

public class CeylonLog extends Log {

    /** The number of errors reported by the typechecker */
    private int numCeylonAnalysisErrors;
    /** The number of exceptions throw by the code generator */
    private int numCeylonCodegenException;
    /** The number of erroneous messages (i.e. bugs) issued by the code generator */
    private int numCeylonCodegenErroneous;
    /** 
     * The number of error messages due neither to code gen exceptions 
     * not erroneous usage (this includes, in particular, 
     * messages generated by later phases of javac). These are usually becuase 
     * codegen produced a tree of garbage.
     */
    private int numCeylonCodegenGarbage;
    /** The number of errors reported by the javac (=> bugs in codegen) */
    private int numNonCeylonErrors;

    private SourceLanguage sourceLanguage;
    private StatusPrinter sp;
    
    /** Get the Log instance for this context. */
    public static Log instance(Context context) {
        Log instance = context.get(logKey);
        if (instance == null)
            instance = new CeylonLog(context);
        return instance;
    }

    /**
     * Register a Context.Factory to create a JavacFileManager.
     */
    public static void preRegister(final Context context) {
        context.put(logKey, new Context.Factory<Log>() {
            @Override
            public Log make(Context c) {
                return new CeylonLog(c);
            }
        });
    }

    protected CeylonLog(Context context) {
        super(context);
        sourceLanguage = SourceLanguage.instance(context);
        Options options = Options.instance(context);
        boolean isProgressPrinted = options.get(Option.CEYLONPROGRESS) != null && StatusPrinter.canPrint();
        if(isProgressPrinted){
            sp = LanguageCompiler.getStatusPrinterInstance(context);
        }else{
            sp = null;
        }
    }

    @Override
    public void report(JCDiagnostic diagnostic) {
        String messageKey = diagnostic.getCode();
        if (messageKey != null) {
            if (messageKey.startsWith("compiler.err.ceylon.codegen.exception")) {
                numCeylonCodegenException++;
            } else if (messageKey.startsWith("compiler.err.ceylon.codegen.erroneous")) {
                numCeylonCodegenErroneous++;
            } else if (messageKey.startsWith("compiler.err.ceylon")) {
                numCeylonAnalysisErrors++;
            } else if (sourceLanguage.isCeylon()) {
                numCeylonCodegenGarbage++;
            } else {
                numNonCeylonErrors++;
            }
        } else if (sourceLanguage.isCeylon()) {
            numCeylonCodegenGarbage++;
        } else {
            numNonCeylonErrors++;
        }
        DiagnosticSource source = diagnostic.getDiagnosticSource();
        if(source != null){
            JavaFileObject file = source.getFile();
            if(file instanceof CeylonFileObject && diagnostic.getType() == DiagnosticType.ERROR){
                ((CeylonFileObject)file).addError(diagnostic);
            }
        }
        super.report(diagnostic);
    }
    
    @Override
    public void note(JavaFileObject file, String key, Object... args) {
        // Ignore lint warnings
    }

    @Override
    public void mandatoryNote(JavaFileObject file, String key, Object... args) {
        // Ignore lint warnings
    }

    @Override
    public void warning(String key, Object... args) {
        // remove warnings for Java 7 classes
        if("big.major.version".equals(key)){
            return;
        }
        super.warning(key, args);
    }

    /** 
     * The number of errors logged due to uncaught exceptions or makeErroneous() 
     * calls during ceylon codegen.  
     */
    public int getCeylonCodegenBugCount() {
        return numCeylonCodegenException + numCeylonCodegenErroneous;
    }
    
    /** 
     * The number of errors logged due to uncaught exceptions
     * calls during ceylon codegen.  
     */
    public int getCeylonCodegenExceptionCount() {
        return numCeylonCodegenException;
    }
    
    /** 
     * The number of errors logged due to codegen producing a bad tree
     */
    public int getCeylonCodegenGarbageTreeCount() {
        return numCeylonCodegenGarbage;
    }
    
    /** 
     * The number of errors logs due to messageful makeErroneous() 
     * calls during ceylon codegen.  
     */
    public int getCeylonCodegenErroneousCount() {
        return numCeylonCodegenErroneous;
    }
    
    /**
     * A count of the number of non-ceylon errors
     */
    public int getNonCeylonErrorCount() {
        return numNonCeylonErrors;
    }
    
    /**
     * A count of the number of Ceylon errors
     */
    public int getCeylonErrorCount(){
        return numCeylonAnalysisErrors;
    }

    @Override
    protected void writeDiagnostic(JCDiagnostic diag) {
        // make sure we clear the progress line if we have one
        if(sp != null)
            sp.clearLine();
        super.writeDiagnostic(diag);
    }
}
